package com.qf6.service.impl;

import com.qf6.domain.Account;
import com.qf6.mapper.AccountMapper;
import com.qf6.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
//@Transactional  //当前类下所有的方法都进行事务控制
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountMapper accountMapper;

    /**
     * mysql事务：
     *    作用：一种安全机制，保障一组关联的增删改操作要么一起成功，要么一起失败
     *
     *    ACID：原子性、一致性、隔离性、持久性
     *
     *    隔离性-隔离级别：
     *      READ-UNCOMMITTED(读未提交)
     *      READ-COMMITTED(读已提交)  -- oracle默认的隔离级别（RC）
     *      REPEATABLE-READ（可重复读） -- mysql默认隔离级别（RR）
     *      SERIALIZABLE（串行化）
     *
     *   读问题：
     *      脏读：一个事务读取到了另一个事务未提交数据
     *      不可重复读: 一个事务在前后两次读取时，读到的数据不一样
     *      幻读：一个事务读到了另一个事务的新增和删除操作，导致在这个事务的读取过程中，某些记录一会有一会没有
     *          在现在mysql使用的innodb数据库引擎中，引入了间隙锁，保障事务读取数据过程中不会出现幻读
     *
     *  mysql中事务处理方式：
     *
     *     try{
     *         //开启事务 ①   conn.setAutoCommit(false)
     *
     *         //执行关联操作 ②
     *
     *         //事务提交  ③  conn.commit()
     *     }catch(Exception e){
     *         //事务回滚  ③  conn.rollback()
     *     }
     */

    /**
     * 数据库的隔离级别和spring中的事务隔离级别是不是一样？  是
     */
//    @Transactional(isolation = Isolation.DEFAULT)

    /**
     * 是否为只读事务。只读事务和非只读事务在spring中的判断逻辑不一样的，代码的性能开销也是不一样的
     * 所以如果在查询方法上进行了事务控制，就需要将事务的readOnly设置为true
     */
//    @Transactional(readOnly = true)

    /**
      spring事务，默认情况下，只针对运行期异常和Error错误进行回滚。对于编译期异常不回滚
        一般我们在进行事务控制时，会将事务的回滚异常设置为Exception.class 包含编译期异常和运行期异常
     */
//    @Transactional(rollbackFor = Exception.class)

    /**
        事务传播行为：
             REQUIRED(0), 默认值，和大业务方法保持在同一个事务中
             SUPPORTS(1), 子业务支持事务，如果大业务带事务，子业务加入大业务的事务，如果不带，没有事务
             MANDATORY(2), 子业务要求大业务必须带事务
             *****REQUIRES_NEW(3) 如果在一个大的业务中，有很多子业务，每个子业务的结果要互不影响，必须给子业务配置上传播行为为这个值
             NOT_SUPPORTED(4), 子业务不支持事务，如果大业务带事务，不加入，如果大业务不带事务，没有事务
             NEVER(5), 子业务要求大业务必须不带事务
             NESTED(6);

     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void transfer(Double money, String inName, String outName) throws Exception {
        //转入
        accountMapper.updateMoneyByName(money,inName);

//        int i = 1/0;
//        if (true)
//            throw new Exception();

        //转出
        accountMapper.updateMoneyByName(-money,outName);
    }

    @Transactional
    @Override
    public void go(Double money, String inName, String outName) throws Exception {

        //第一个子业务（进行转账）
        accountService.transfer(money,inName,outName);
        //第二个子业务，记录转账日志（即时失败，也不应该影响第一个子业务的结果）
        accountService.saveLog();
    }



    /**
     * 记录转账日志的业务
     * 这个业务的结果不应该影响上述转账业务结果
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void saveLog() {

        int i = 1/0;
    }

    public List<Account> findAll(){
        return null;
    }
}
